package com.summer.data.apt;

import com.summer.data.table.BaseTable;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic;

import java.util.HashMap;
import java.util.Map;

import static com.summer.data.apt.Constant.COLUMN_DATA_MAP;
import static com.summer.data.apt.Constant.DATA_COLUMN_MAP;
import static javax.tools.Diagnostic.Kind.NOTE;

/**
 * @author ydfqy
 */
public class TableTreeTranslator extends TreeTranslator {

    protected Messager messager;
    protected JavacTrees trees;
    protected TreeMaker treeMaker;
    protected Names names;

    TreeHelper treeHelper;

    protected JCTree.JCClassDecl classDecl;

    JCClassDecl innerClassDecl;

    List<JCTree> innerClassMemberDecls = List.nil();

    public TableTreeTranslator(
            Messager messager,
            JavacTrees classTree,
            TreeMaker treeMaker,
            Names names,
            Element element) {
        this.messager = messager;
        this.trees = classTree;
        this.treeMaker = treeMaker;
        this.names = names;
        treeHelper = new TreeHelper(messager, classTree, treeMaker, names);
    }

    @Override
    public void visitClassDef(JCTree.JCClassDecl classDecl) {
        this.classDecl = classDecl;
        super.visitClassDef(classDecl);
        if (!ClassUtil.innerClassExists(BaseTable.ENTITY_CLASS_NAME, classDecl)) {
            makeInnerClassDecl();
        }
        addShareTableDecl();
//        addStaticBlock();
    }

    /**
     * 生成内部的 Entity 类
     */
    private void makeInnerClassDecl() {
        innerClassDecl = treeHelper.classDef(
            Flags.PUBLIC | Flags.FINAL | Flags.STATIC,
                BaseTable.ENTITY_CLASS_NAME
        );

        // 创建成员变量： private Map<String, Boolean> ENTITY_MAP_NAME = new HashMap<>();
        makeInnerMapVarDecl();

        for (JCTree memberDecl : classDecl.defs) {
            if (memberDecl instanceof  JCTree.JCVariableDecl) {
                JCTree.JCVariableDecl varDecl = (JCTree.JCVariableDecl) memberDecl;
                // 只有 XxxColumn 类型才需要生成
                if (COLUMN_DATA_MAP.containsKey(varDecl.vartype.type.toString())) {
                    makeInnerVarDecl(varDecl);
                    makeSetterMethodDecl(varDecl);
                    makeGetterMethodDecl(varDecl);
                }
            }
        }

        innerClassDecl.defs = innerClassDecl.defs.appendList(innerClassMemberDecls);
        classDecl.defs = classDecl.defs.append(innerClassDecl);
    }

    public static final String ENTITY_MAP_NAME = "__DIRTY_MAP";

    /**
     * 生成 Entity 的 __DIRTY_MAP 属性
     */
    private void makeInnerMapVarDecl() {

        /*
        private Map<String, Boolean> ENTITY_MAP_NAME = new HashMap<>();
         */

        // new HashMap<>();
        JCTree.JCNewClass newHashMap = treeMaker.NewClass(
                null,                      // 不太明白此参数的含义，我看很多例子中此参数都设置为null
                List.nil(),                           // 泛型参数类型列表
                treeHelper.typeRef(HashMap.class),                              // 待创建对象的类型
                List.nil(),                                // 参数列表
                null                                   // 类定义
        );

        JCTree.JCVariableDecl newVarDecl = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PRIVATE),             // 访问标识
                names.fromString(ENTITY_MAP_NAME),                                // 变量名
                treeHelper.typeRef(Map.class),                             // 变量类型
                newHashMap                                            // 初始化表达式
        );
        innerClassMemberDecls = innerClassMemberDecls.append(newVarDecl);
    }

    /**
     * 根据 Table 定义生成 Entity 的属性
     * @param varDecl ，Column 列定义
     */
    private void makeInnerVarDecl(JCTree.JCVariableDecl varDecl) {

        JCTree.JCVariableDecl newVarDecl = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PRIVATE),             // 访问标识
                varDecl.name,                                // 变量名
                treeHelper.column2DataType(varDecl),                             // 变量类型
                null                                            // 初始化表达式
        );
        innerClassMemberDecls = innerClassMemberDecls.append(newVarDecl);
    }

    /**
     * 生成 setter 方法
     * @param varDecl 变量定义
     */
    private void makeSetterMethodDecl(JCTree.JCVariableDecl varDecl) {

//         public void setName(String name) {
//             this.ENTITY_MAP_NAME.put(name, true);
//             this.name = name;
//         }

        String setterMethodName = ClassUtil.toSetterName(varDecl);
        if (ClassUtil.methodExists(setterMethodName, classDecl)) {
            return;
        }

        // this.ENTITY_MAP_NAME
        JCTree.JCFieldAccess thisENTITY_MAP_NAME = treeMaker.Select(
                treeMaker.Ident(names.fromString("this")), names.fromString(ENTITY_MAP_NAME)
        );

        // this.ENTITY_MAP_NAME.put(name, true);
        JCTree.JCMethodInvocation apply = treeMaker.Apply(
                List.nil(),
                treeMaker.Select(thisENTITY_MAP_NAME, names.fromString("put")),
                List.of(
                        treeMaker.Literal(varDecl.getName().toString()),
                        treeMaker.Literal(true)
                )
        );

        // this.xxx
        JCTree.JCFieldAccess thisX = treeMaker.Select(treeMaker.Ident(names.fromString("this")), varDecl.getName());
        // this.xxx = xxx
        JCTree.JCAssign assign = treeMaker.Assign(thisX, treeMaker.Ident(varDecl.getName()));


        // this.xxx = xxx;
        List<JCTree.JCStatement> statements = List.of(
                treeMaker.Exec(apply),
                treeMaker.Exec(assign)
        );

        JCTree.JCBlock body = treeMaker.Block(0, statements);

        // 返回值
        JCTree.JCExpression methodReturnType = treeMaker.Type(new Type.JCVoidType());

        // 生成方法参数之前，指明当前语法节点在语法树中的位置，避免出现异常 java.lang.AssertionError: Value of x -1
        treeMaker.pos = varDecl.pos;
        JCTree.JCVariableDecl param = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER),
                treeHelper.toName(varDecl.name.toString()),
                treeHelper.column2DataType(varDecl),
                null
        );

        JCTree.JCMethodDecl methodDecl = treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),          // mods：访问标志
                names.fromString(setterMethodName),         // name：方法名
                methodReturnType,                           // restype：返回类型
                List.nil(),                                 // typarams：泛型参数列表
                List.of(param),                                 // params：参数列表
                List.nil(),                                 // thrown：异常声明列表
                body,                                       //方法体
                null                                        // init
        );
        innerClassMemberDecls = innerClassMemberDecls.append(methodDecl);
    }

    /**
     * 生成 getter 方法
     * @param varDecl ：Column 定义
     */
    private void makeGetterMethodDecl(JCTree.JCVariableDecl varDecl) {

        String getterMethodName = ClassUtil.toGetterName(varDecl);

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();

        statements.append(
            treeMaker.Return(
                treeMaker.Select(
                    treeMaker.Ident(names.fromString("this")), varDecl.getName()
                )
            )
        );

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        JCTree.JCMethodDecl methodDecl = treeMaker.MethodDef(
            treeMaker.Modifiers(Flags.PUBLIC),          // mods：访问标志
            names.fromString(getterMethodName),                   // name：方法名
            treeHelper.column2DataType(varDecl),                     // restype：返回类型
            List.nil(),                                 // typarams：泛型参数列表
            List.nil(),                                 // params：参数列表
            List.nil(),                                 // thrown：异常声明列表
            body,                                       //方法体
            null                                        // init
        );
        innerClassMemberDecls = innerClassMemberDecls.append(methodDecl);
    }


    /**
     * 生成 Table 的 XxxColumn 方法
     * @param varDecl 变量定义
     */
    private void makeColumnMethodDecl(JCTree.JCVariableDecl varDecl) {
        Name name = varDecl.getName();
        String fieldType = varDecl.getType().toString();
        String clazzName = DATA_COLUMN_MAP.get(fieldType);
        if (clazzName == null) {
            return;
        }

        messager.printMessage(NOTE, "apt 创建方法: " + name, varDecl.sym);
        // 待创建对象的类型
        JCTree.JCExpression typeRef = treeHelper.typeRef(clazzName);

        // 生成入参: "xxx", this
        List<JCTree.JCExpression> params = List.of(
                treeMaker.Literal(name.toString()),
                treeMaker.Ident(names.fromString("this"))
        );

        // new StringColumn("xxx", this)
        JCTree.JCNewClass newColumn = treeMaker.NewClass(
                null,                      // 不太明白此参数的含义，我看很多例子中此参数都设置为null
                List.nil(),                           // 泛型参数类型列表
                typeRef,                              // 待创建对象的类型
                params,                                // 参数列表
                null                                   // 类定义
        );

        // return new StringColumn("xxx", this);
        JCTree.JCReturn jcReturn = treeMaker.Return(newColumn);

        // 函数 block 块
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(jcReturn);
        JCTree.JCBlock block = treeMaker.Block(0, statements.toList());

        // 完整的函数声明
        JCTree.JCMethodDecl methodDecl = treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),                          //mods：     访问标志
                name,                                                       //name：     方法名
                typeRef,                                    //restype：  返回类型
                List.nil(),                                 //typarams： 泛型参数列表
                List.nil(),                                 //params：   参数列表
                List.nil(),                                 //thrown：   异常声明列表
                block,//方法体
                null
        );

        innerClassMemberDecls = innerClassMemberDecls.append(methodDecl);

    }

    /**
     * 为 Table 添加 shareTable 变量
     */
    private void addShareTableDecl() {

        String shareTableVarName = ClassUtil.getShareTableName(classDecl);
        // new XxxTable()
        JCTree.JCNewClass shareTableInitDecl = treeMaker.NewClass(
                null,                      // 不太明白此参数的含义，我看很多例子中此参数都设置为null
                List.nil(),                           // 泛型参数类型列表
                treeMaker.Ident(classDecl.name),                              // 待创建对象的类型
                List.nil(),                                // 参数列表
                null                                   // 类定义
        );

        // public static XxxTable xxxTable = new XxxTable();
        JCTree.JCVariableDecl shareTableDecl = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PUBLIC|Flags.STATIC),              // 访问标识
                treeHelper.toName(shareTableVarName),                                 // 变量名
                treeMaker.Ident(classDecl.name),                // 变量类型
                shareTableInitDecl                                            // 初始化表达式
        );

        classDecl.defs = classDecl.defs.append(shareTableDecl);
    }

//
//    /**
//     * 添加静态代码块
//     */
//    private void addStaticBlock() {
//
//        /*
//        static {
//            BaseTable.regTable2Entity(XxxTable.class, XxxTable.Entity.class)
//        }
//        */
//
//        // 生成方法参数之前，指明当前语法节点在语法树中的位置，避免出现异常 java.lang.AssertionError: Value of x -1
//        treeMaker.pos = classDecl.pos;
//
//        // BaseTable
//        JCTree.JCExpression baseTableDef = treeHelper.typeRef(BaseTable.class);
//
//        // BaseTable.regTable2Entity
//        ClassUtil.getMethodDecl("regTable2Entity", BaseTable.class)
//        JCTree.JCFieldAccess access = treeMaker.Select(
//                baseTableDef,
//                treeMaker.Select(
//                        treeMaker.Ident(classDecl.name), names.fromString("class")
//                )
//        );
//
//        // XxxTable.class
//        JCTree.JCFieldAccess param1 = treeMaker.Select(
//                treeMaker.Ident(classDecl.name), names.fromString("class")
//        );
//
//        // XxxTable.Entity
//        JCTree.JCFieldAccess entityAccess = treeMaker.Select(
//                treeMaker.Ident(classDecl.name), innerClassDecl.name
//        );
//
//        // XxxTable.Entity.class
//        JCTree.JCFieldAccess param2 = treeMaker.Select(
//                entityAccess, names.fromString("class")
//        );
//
//        JCTree.JCMethodInvocation apply = treeMaker.Apply(
//                List.nil(),
//                access,
//                List.of(param1, param2)
//        );
//
////        classDecl
//
//        // 函数 block 块
//        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
//        statements.append(treeMaker.Exec(apply));
//        JCTree.JCBlock block = treeMaker.Block(Flags.STATIC, statements.toList());
//
//
//
//        classDecl.defs = classDecl.defs.append(block);
//    }

}
